<template>
    <div :class="['upload-content', multipleReal ? '' : 'upload-content-singla']">
        <div class="upload-box">
            <el-upload
                ref="certificateUploadRef"
                v-model:file-list="fileList"
                :class="['avatar-uploader', isShowUpload ? '' : 'hidden-upload', disabled ? 'disabled' : '']"
                list-type="picture-card"
                :accept="accept"
                :disabled="disabled"
                :multiple="multipleReal"
                :limit="limitNumReal"
                :before-upload="beforeUploadHandle"
                :on-exceed="onExceedHandle"
                :http-request="uploadImg">
                <div class="upload-btn">
                    <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
                    <div vif="uploadLabel" class="upload-btn-label">{{ uploadLabel }}</div>
                </div>
                <template #file="{ file }">
                    <div class="preview-image">
                        <el-image
                            :id="file.url.substr(-6)"
                            :src="file.url"
                            :preview-src-list="fileList.map(ite => ite.url)"
                            fit="cover">
                            <template #error>
                                <el-image :src="image_default_logo" fit="cover" />
                            </template>
                        </el-image>
                        <span class="el-upload-list__item-actions">
                            <span class="el-upload-list__item-preview" @click="previewImage(file.url.substr(-6))">
                                <el-icon><View /></el-icon>
                            </span>
                            <span v-if="!disabled" class="el-upload-list__item-delete" @click="delImage(file)">
                                <el-icon><Delete /></el-icon>
                            </span>
                        </span>
                    </div>
                </template>
            </el-upload>
            <div v-if="uploadTips" class="upload-tips">{{ uploadTips }}</div>
        </div>
        <img
            v-if="!multipleReal"
            class="upload-placeholder"
            src="https://img.static-keyid.cn/toolspace/scrm/2023/10/1697700125704iqltntuym75wi2de.png" />
    </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, getCurrentInstance } from 'vue';
import type { PropType } from 'vue';
import { ElMessage, ElUpload, ElIcon, ElImage } from 'element-plus';
import { Plus, Delete, View } from '@element-plus/icons-vue';
import { applyUploadParam, uploadImageToOss, getOcrInfo, resolveAddress } from '../api/ZwUnityKitCertificateUpload';
// import { PropsType, EmitType } from './type';

defineOptions({
    name: 'ZwUnityKitCertificateUpload'
});

interface ImageDataType {
    code: string;
    url: string;
}

interface PropsType {
    /** v-model绑定值，内容为图片code 单选时为string 多选时为string数组 */
    modelValue: string | string[];
    /**
     * 上传的图片URL地址，支持单张和多张
     * 单张时数据类型为string，多张时为array，里面对象需要包含code和url
     * 比如[{code: "xxx", url: "https://xxx.jpg"}]
     */
    imageList?: ImageDataType | PropType<Array<ImageDataType>> | null;
    /** 文件不符合限制条件时的提示文案 */
    validateTips?: string;
    /** 上传按钮文字 */
    uploadLabel?: string;
    /** 上传提示文案 */
    uploadTips?: string;
    /** 允许上传文件的最大数量，默认为1，选择ocr识别时限制为1 */
    limitNum?: number;
    /** 允许上传文件的大小限制，默认为1（M） */
    limitSize?: number;
    /** 图片的格式限制，默认为 JPG、BMP、PNG */
    limitFormat?: Array<string>;
    /**
     * ocr类型，默认为空，此时，即不需要ocr识别，上传图片数自动限制为1张，传参后通过submit事件返回识别到的数据
     * RESIDENT_CARD_NATIONAL_EMBLEM 身份证国徽面
     * RESIDENT_CARD_NATIONAL_HEAD 身份证人头面
     * LICENSE 营业执照
     * BANK_ACCOUNT_PERMIT 开户许可
     * BANK_CARD 银行卡
     */
    ocrType?: string;
    /** 是否多选，默认单选，选择ocr识别时限制为单选 */
    multiple?: boolean;
    /** 是否禁用上传 */
    disabled?: boolean;
}

interface EmitType {
    /**
     * modelValue数据双向绑定
     */
    (e: 'update:modelValue', data: string | string[] | null): void;
    /**
     * 选中的数据
     * 单选时为对象，包含code和url属性
     * 多选时为数组，包含code和url属性的对象
     */
    (e: 'update:imageList', data: ImageDataType | (PropType<Array<ImageDataType>> | any) | null): void;
    /**
     * 上传完成，返回最新的上传数据
     */
    (e: 'change', val: any): void;
    /**
     * 上传完成，且ocr识别完成后返回识别到的数据
     */
    (e: 'submit', val: any): void;
    /**
     * 开始上传事件
     */
    (e: 'start', val: boolean): void;
    /**
     * 上传结束事件
     */
    (e: 'finished', val: boolean): void;
}

const props = withDefaults(defineProps<PropsType>(), {
    modelValue: '',
    imageList: null,
    validateTips: '',
    uploadLabel: '',
    uploadTips: '',
    limitNum: 1,
    limitSize: 1,
    limitFormat: () => ['JPG', 'BMP', 'PNG'],
    ocrType: '',
    multiple: false,
    disabled: false
});

const image_default_logo = ref<string>(
    'https://img.static-keyid.cn/toolspace/scrm/2023/09/16957192102834z1kkp6piit8zpzb.png'
);
const app = getCurrentInstance();
const certificateUploadRef = ref<null | (InstanceType<typeof HTMLDivElement> & any)>(null);
const fileList = ref<Array<any>>([]);

const multipleReal = computed(() => {
    if (props.ocrType || props.limitNum === 1) {
        return false;
    } else {
        return props.multiple;
    }
});

const limitNumReal = computed(() => {
    if (props.ocrType || !multipleReal.value) {
        return 1;
    } else {
        return props.limitNum;
    }
});

const accept = computed(() => {
    if (props.limitFormat.length) {
        return props.limitFormat.reduce((pre: string, cur: string) => {
            const newFormat = `.${getLowCase(cur)}, .${getUpCase(cur)}`;
            return pre ? `${pre}, ${newFormat}` : newFormat;
        }, '');
    } else {
        return '.jpg, .png, .jpeg, .gif';
    }
});

const isShowUpload = computed(() => {
    return fileList.value.length < limitNumReal.value;
});

const emit = defineEmits<EmitType>();

/**
 * @description: 文件上传前处理
 * @param {*} file
 * @return {*}
 */
const beforeUploadHandle = file => {
    const isLimitSize = file.size / 1024 / 1024 < props.limitSize;
    if (!isLimitSize) {
        let msg = `最大支持上传${props.limitSize}M`;
        if (props.validateTips) {
            msg = props.validateTips;
        }
        ElMessage.warning(msg);
        return false;
    }
    return true;
};

/**
 * @description: 文件超过限制的回调
 * @param {*} files
 * @return {*}
 */
const onExceedHandle = files => {
    if (multipleReal.value) {
        if (limitNumReal.value < files.length) {
            let msg = `最多支持上传${limitNumReal.value}张`;
            if (props.validateTips) {
                msg = props.validateTips;
            }
            ElMessage.warning(msg);
        }
    }
};

/**
 * @description: 点击预览大图
 * @param {*} id
 * @return {*}
 */
const previewImage = id => {
    const imgDom = document.getElementById(id);
    imgDom?.click();
};

/**
 * @description: 删除图片
 * @param {*} file
 * @return {*}
 */
const delImage = file => {
    certificateUploadRef.value.handleRemove(file);
    if (multipleReal.value) {
        emit(
            'update:modelValue',
            fileList.value.map((ite: any) => ite.code)
        );
        emit(
            'update:imageList',
            fileList.value.map((ite: any) => ({ code: ite.code, url: ite.url }))
        );
        emit(
            'change',
            fileList.value.map((ite: any) => ({ code: ite.code, url: ite.url }))
        );
    } else {
        emit('update:modelValue', '');
        emit('update:imageList', null);
        emit('change', null);
        emit('submit', null);
    }
};

const statusEmit = (step: number, flag = true) => {
    switch (step) {
        case 1:
            emit('start', flag);
            break;
        case 2:
            emit('finished', flag);
            break;
    }
};

/**
 * @description: 请求上传图片
 * @param {*} options
 * @return {*}
 */
const uploadImg = async options => {
    statusEmit(1);
    const { file } = options;
    try {
        const res = await applyUploadParam(app, { fileName: file.name });
        await uploadImageToOssFn(file, res.data);
    } catch (err) {
        statusEmit(2, false);
    }
};

/**
 * @description: 图片上传到oss
 * @param {*} file
 * @param {*} res
 * @return {*}
 */
const uploadImageToOssFn = async (file, res) => {
    const { OSSAccessKeyId, key, policy, callback, signature, expire, host } = res;
    const formData = new FormData();
    formData.append('OSSAccessKeyId', OSSAccessKeyId);
    formData.append('key', key);
    formData.append('policy', policy);
    formData.append('callback', callback);
    formData.append('signature', signature);
    formData.append('expire', expire);
    formData.append('success_action_status', '200');
    formData.append('file', file);
    try {
        const ress = await uploadImageToOss(host, formData);
        if (multipleReal.value) {
            fileList.value.some(item => {
                if (item.name === file.name) {
                    item.code = ress.data.code;
                    item.url = ress.data.url;
                    return true;
                } else {
                    return false;
                }
            });
            emit(
                'update:modelValue',
                fileList.value.map((ite: any) => ite.code)
            );
            emit(
                'update:imageList',
                fileList.value.map((ite: any) => ({ code: ite.code, url: ite.url }))
            );
            emit(
                'change',
                fileList.value.map((ite: any) => ({ code: ite.code, url: ite.url }))
            );
        } else {
            emit('update:modelValue', ress.data.code as string);
            emit('update:imageList', { code: ress.data.code, url: ress.data.url });
            emit('change', { code: ress.data.code, url: ress.data.url });
        }
        if (props.ocrType) {
            try {
                await getOcr(ress.data.code);
            } catch (err) {
                statusEmit(2, false);
            }
        } else {
            statusEmit(2, true);
        }
    } catch (err) {
        statusEmit(2, false);
    }
};

/**
 * @description: 图片ocr识别
 * @param {*} file_code
 * @return {*}
 */
const getOcr = async file_code => {
    const ocr_type = getOcrType();
    try {
        const res = await getOcrInfoFn(file_code, ocr_type);
        if (res && res.data) {
            emit('submit', res.data);
            statusEmit(2, true);
        } else {
            certificateUploadRef.value.clearFiles();
            statusEmit(2, false);
        }
    } catch (err) {
        statusEmit(2, false);
    }
};

/**
 * @description: 获取ocr识别信息
 * @param {*} file_code
 * @param {*} ocr_type
 * @return {*}
 */
const getOcrInfoFn = async (file_code, ocr_type) => {
    try {
        const res = await getOcrInfo(app, { file_code, ocr_type });
        let address = '';
        if (props.ocrType === 'LICENSE') {
            const { businessAddress } = res.data;
            businessAddress && (address = businessAddress);
        }
        if (props.ocrType === 'RESIDENT_CARD_NATIONAL_HEAD') {
            const { address: address_ } = res.data;
            address_ && (address = address_);
        }
        if (address) {
            const rr = await resolveAddress(app, { address });
            if (rr && rr.data) {
                const { code, address_components } = rr.data;
                if (code.length) {
                    if (code.length === 2) {
                        const mid = `${code[0].slice(0, 3)}100`;
                        code.splice(1, 0, mid);
                    }
                    res.data.area = code.join('/');
                    res.data.address_components = address_components;
                }
            }
        }
        return res;
    } catch (err) {
        statusEmit(2, false);
    }
};

/**
 * @description: 获取ocr类型
 * @return {*}
 */
const getOcrType = () => {
    let ocr_type = 0;
    switch (props.ocrType) {
        case 'RESIDENT_CARD_NATIONAL_EMBLEM':
            ocr_type = 12;
            break;
        case 'RESIDENT_CARD_NATIONAL_HEAD':
            ocr_type = 11;
            break;
        case 'LICENSE':
            ocr_type = 20;
            break;
        case 'BANK_ACCOUNT_PERMIT':
            ocr_type = 30;
            break;
        case 'BANK_CARD':
            ocr_type = 40;
            break;
    }
    return ocr_type;
};

/**
 * @description: 转化小写
 * @param {*} str
 * @return {*}
 */
const getLowCase = str => {
    return str.toLowerCase();
};

/**
 * @description: 转化大写
 * @param {*} str
 * @return {*}
 */
const getUpCase = str => {
    return str.toUpperCase();
};

watch(
    () => props.imageList,
    () => {
        if (props.imageList && props.imageList instanceof Array && props.imageList.length) {
            if (!fileList.value.length) {
                fileList.value.splice(0);
                fileList.value.push(...props.imageList);
            }
        } else if (props.imageList) {
            const { code = '', url = '' } = props.imageList as ImageDataType;
            if (code && url) {
                if (!fileList.value.length) {
                    fileList.value.splice(0);
                    fileList.value.push({ code, url });
                }
            }
        }
    },
    { immediate: true, deep: true }
);
</script>

<style lang="scss">
@import './Index';
</style>
